fun read file =
    let
        val inStream = TextIO.openIn file
    in
        (* TextIO.inputAll returns a TextIO.vector, which is a string. *)
        TextIO.inputAll inStream
    end;



val newline_tokenizer = String.tokens (fn c => c = #"\n");
val space_tokenizer = String.tokens (fn c => c = #" ");

datatype HorizontalChange = HorizontalChange of int;
datatype DepthChange = DepthChange of int;
type change = HorizontalChange * DepthChange;


fun direction_to_factor dir =
    case dir of
        "forward" => 1
      | "backward" => ~1
      | "up" => ~1
      | "down" => 1
      | _ => raise Fail "unrecognized direction";


fun calc_change line =
    let
        val (dir::len_as_str::_) = space_tokenizer line;
        val len = case Int.fromString len_as_str of
                      SOME number => number
                    | NONE => raise Fail "encountered a non-int string";
        val dir_factor = direction_to_factor dir;
    in
        case dir of
            "forward" => (HorizontalChange (dir_factor * len), DepthChange 0)
          | "backward" => (HorizontalChange (dir_factor * len), DepthChange 0)
          | "up" => (HorizontalChange 0, DepthChange (dir_factor * len))
          | "down" => (HorizontalChange 0, DepthChange (dir_factor * len))
          | _ => raise Fail "unrecognized direction"
    end;


fun plus_changes (change1, change2) =
    let
        val (HorizontalChange h1, DepthChange d1) = change1;
        val (HorizontalChange h2, DepthChange d2) = change2;
    in
        (HorizontalChange (h1 + h2), DepthChange (d1 + d2))
    end;


val initial_change = (HorizontalChange 0, DepthChange 0): change;
val str = read "input";
val lines = newline_tokenizer str;

let
    val (HorizontalChange h, DepthChange d) =
        foldl (fn (line, acc) => plus_changes (acc, (calc_change line)))
              initial_change
              lines;
    val solution = h * d;
in
    solution
end;
